package com.coldlz.mmp.ssl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;

import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyPairGenerator;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;

public class RootCertificateHolder {
    public static final String COMMON_NAME_ROOT = "ZYC_CA_ROOT";
    public static final String CLASS_PATH_ROOT_CERT = "/config/root.crt";
    public static final String CLASS_PATH_ROOT_PRIVATE = "/config/root-private.key";
    public static final String CLASS_PATH_ROOT_PUBLIC = "/config/root-public.key";
    private static final KeyPairStr rootKeyPair;
    private static final X509Certificate rootCert;
    private static final CASignerProvider caSignerProvider;


    static {
        StopWatch stopWatch = new StopWatch("init RootCertificateHolder");
        stopWatch.start("loadRootKeyPair");
        rootKeyPair = loadRootKeyPair();
        stopWatch.stop();
        stopWatch.start("loadRootCert");
        rootCert = loadRootCert();
        stopWatch.stop();
        stopWatch.start("createRootSignerProvider");
        caSignerProvider = createRootSignerProvider();
        stopWatch.stop();
        //System.out.println(stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
    }

    public static KeyPairStr getRootKeyPair() {
        return rootKeyPair;
    }

    public static X509Certificate getRootCert() {
        return rootCert;
    }

    public static KeyPairStr createRSAKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize);
        return new KeyPairStr(keyPairGenerator.generateKeyPair());
    }

    public static X509Certificate createRootCert(KeyPairStr keyPairStr) throws Exception {

        // 构建证书信息，包含公钥
        X500Name issuer = createX500Name(COMMON_NAME_ROOT);// 签发人
//        BigInteger serial = BigInteger.probablePrime(32, new Random());// 证书序列号
        BigInteger serial = new BigInteger("-1378661553");// 固定证书序列号模拟每次生成的都是一样的
        Date notBefore = DateUtil.date(LocalDateTime.of(2024, 1, 1, 0, 0, 0)).toJdkDate();
        Date notAfter = new Date(notBefore.getTime() + 10 * 365 * 24 * 3600 * 1000L);
        X500Name subject = createX500Name(COMMON_NAME_ROOT);// 证书的主体信息
        ASN1InputStream asn1InputStream = new ASN1InputStream(keyPairStr.getPublicKey());
        SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(asn1InputStream.readObject());
        asn1InputStream.close();
        X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuer, serial, notBefore, notAfter, subject, publicKeyInfo);
        builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
        int usage = KeyUsage.digitalSignature
                | KeyUsage.nonRepudiation
                | KeyUsage.keyEncipherment
                | KeyUsage.dataEncipherment
                | KeyUsage.keyAgreement
                | KeyUsage.keyCertSign
                | KeyUsage.cRLSign
                | KeyUsage.encipherOnly
                | KeyUsage.decipherOnly;
        builder.addExtension(Extension.keyUsage, true, new KeyUsage(usage));


        // 所有颁发策略
        KeyPurposeId[] keyPurposeIds = {
                KeyPurposeId.id_kp_clientAuth // 向远程计算机证明你的身份
                , KeyPurposeId.id_kp_codeSigning // 确保软件来自软件发布者、保护软件在发行后不被更改
                , KeyPurposeId.id_kp_emailProtection // 保护电子邮件消息
                , KeyPurposeId.id_kp_ipsecEndSystem // 允许 Internet 上的安全通信
                , KeyPurposeId.id_kp_ipsecTunnel // 允许 Internet 上的安全通信
                , KeyPurposeId.id_kp_ipsecUser // 允许 Internet 上的安全通信
                , KeyPurposeId.id_kp_serverAuth // 保证远程计算机的身份
                , KeyPurposeId.id_kp_timeStamping // 允许用当前时间签名数据

//                , KeyPurposeId.id_kp_OCSPSigning // OCSP 签名
//                , KeyPurposeId.id_kp_dvcs
//                , KeyPurposeId.id_kp_sbgpCertAAServerAuth
//                , KeyPurposeId.id_kp_scvp_responder
//                , KeyPurposeId.id_kp_eapOverPPP
//                , KeyPurposeId.id_kp_eapOverLAN
//                , KeyPurposeId.id_kp_scvpServer
//                , KeyPurposeId.id_kp_scvpClient
//                , KeyPurposeId.id_kp_ipsecIKE
//                , KeyPurposeId.id_kp_capwapAC
//                , KeyPurposeId.id_kp_capwapWTP

//                , KeyPurposeId.id_kp_cmcCA
//                , KeyPurposeId.id_kp_cmcRA
//                , KeyPurposeId.id_kp_cmKGA

//                , KeyPurposeId.id_kp_smartcardlogon // 智能卡登录
//                , KeyPurposeId.id_kp_macAddress
//                , KeyPurposeId.id_kp_msSGC
//                , KeyPurposeId.id_kp_nsSGC
        };
        builder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(keyPurposeIds));
        builder.addExtension(Extension.subjectKeyIdentifier, false, new BcX509ExtensionUtils().createSubjectKeyIdentifier(publicKeyInfo));


        // 利用私钥构建签名器，包含私钥
        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
        AsymmetricKeyParameter privateKeyParameter = PrivateKeyFactory.createKey(keyPairStr.getPrivateKey());
        ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyParameter);


        // 使用签名器对证书信息进行签名 并构建出证书对象
        X509CertificateHolder holder = builder.build(signer);
        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder);

        // 验证证书
        cert.verify(keyPairStr.getPublic());
        return cert;
    }

    private static X500Name createX500Name(String cn) {
        return new X500NameBuilder(X500Name.getDefaultStyle())
                .addRDN(BCStyle.C, "CN")
                .addRDN(BCStyle.ST, "Sichuan")
                .addRDN(BCStyle.L, "Chengdu")
                .addRDN(BCStyle.O, "zfsw")
                .addRDN(BCStyle.OU, "yfb")
                .addRDN(BCStyle.CN, cn)
                .addRDN(BCStyle.E, "378661553@qq.com").build();
    }

    private static URL getUrl(String classPath) {
        URL url = RootCertificateHolder.class.getResource(classPath);
        if (url == null) {
            throw new RuntimeException("路径下没有获取到对应资源:" + classPath);
        }
        return url;
    }

    private static X509Certificate loadRootCert() {
        List<String> base64Str = FileUtil.readLines(getUrl(CLASS_PATH_ROOT_CERT), StandardCharsets.UTF_8);
        return Util.loadCert(base64Str);
    }


    private static KeyPairStr loadRootKeyPair() {
        List<String> publicKeyLines = FileUtil.readLines(getUrl(CLASS_PATH_ROOT_PUBLIC), StandardCharsets.UTF_8);
        List<String> privateKeyLines = FileUtil.readLines(getUrl(CLASS_PATH_ROOT_PRIVATE), StandardCharsets.UTF_8);
        return Util.loadKeyPairStr(privateKeyLines, publicKeyLines);
    }

    private static CASignerProvider createRootSignerProvider() {
        KeyPairStr rootKeyPair = RootCertificateHolder.getRootKeyPair();
        X509Certificate rootCert = RootCertificateHolder.getRootCert();
        return new DefaultCASignerProvider(rootKeyPair, rootCert);
    }

    public static CASignerProvider getRootSignerProvider() {
        return caSignerProvider;
    }
}
